﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Pixysoft.Framework.Reflection.Controller;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Reflection.Core
{
    /// <summary>
    /// dummyType.value = null : object/interface/structure
    /// property.value not null : always
    /// dummyType.elementType.value null : always
    /// dummyType.genericArguments.value null: always
    /// </summary>
    class DummyType : DummyMemberInfo, IDummyType
    {
        string _namespace;
        string fullName;

        IDummyAssembly assembly = null;
        DotNetDataType dataType = DotNetDataType.UNKNOWN;
        DotNetPropertyType propertyType = DotNetPropertyType.Unknown;

        bool isArray = false;
        bool isGenericType = false;

        List<DummyPropertyInfo> properties = new List<DummyPropertyInfo>();
        List<DummyMethodInfo> methods = new List<DummyMethodInfo>();
        List<IDummyType> genericArgs = new List<IDummyType>();
        List<IDummyType> interfaces = new List<IDummyType>();
        IDummyType elementType = null;

        /// <summary>
        /// 需要后期初始化:declaringType, propertyType, datatype, value, attribute, properties
        /// </summary>
        /// <param name="nameSpace"></param>
        /// <param name="fullname"></param>
        /// <param name="name"></param>
        /// <param name="hashcode"></param>
        public DummyType(string nameSpace, string fullname, string name)
        {
            LoggerHelper.Log("-> new DummyType({0},{1},{2})", nameSpace, fullname, name);

            this._namespace = nameSpace;
            this.fullName = fullname;
            this.Name = name;
        }

        public string Namespace
        {
            get { return _namespace; }
            set { _namespace = value; }
        }

        public string FullName
        {
            get { return fullName; }
            set { fullName = value; }
        }

        [Assembly]
        public IDummyAssembly Assembly
        {
            get { return assembly; }
            set
            {
                if (value == null)
                    throw Exceptions.VerificationFailedException(value);

                LoggerHelper.Log("-> DummyType.Assembly = {0};", value);
                assembly = value;
            }
        }

        public DotNetPropertyType PropertyType
        {
            get { return propertyType; }
            set { propertyType = value; }
        }

        public DotNetDataType DataType
        {
            get { return dataType; }
            set
            {
                LoggerHelper.Log("-> DummyType.DataType = {0};", value);
                dataType = value;
            }
        }


        public bool IsArray
        {
            get { return isArray; }
            set { isArray = value; }
        }

        public bool IsGenericType
        {
            get { return isGenericType; }
            set { isGenericType = value; }
        }

        public IDummyType GetElementType()
        {
            return elementType;
        }

        public IDummyType[] GetGenericArguments()
        {
            return genericArgs.ToArray();
        }

        public IDummyType[] GetInterfaces()
        {
            return interfaces.ToArray();
        }

        public bool HasProperty(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name);

            LoggerHelper.Log("-> if(DummyType.HasProperty({0}))", name);
            foreach (IDummyPropertyInfo info in properties)
            {
                if (string.Equals(name, info.Name))
                    return true;
            }
            return false;
        }

        public IDummyPropertyInfo GetProperty(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name);

            LoggerHelper.Log("-> DummyType.GetProperty({0});", name);

            foreach (IDummyPropertyInfo info in properties)
            {
                if (string.Equals(name, info.Name))
                    return info;
            }

            throw new Exception(string.Format("no property existed.type:{0} name:{1}", this.Name, name));
        }

        public IDummyPropertyInfo[] GetProperties()
        {
            return properties.ToArray();
        }

        public IDummyMethodInfo GetMethod(string name, IDummyType[] types)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name, types);

            foreach (DummyMethodInfo method in methods)
            {
                if (!string.Equals(method.Name, name))
                    continue;

                IDummyParameterInfo[] parameters = method.GetParameters();

                if (parameters.Length != types.Length)
                    continue;

                bool founded = true;

                for (int i = 0; i < parameters.Length; i++)
                {
                    IDummyParameterInfo parameter = parameters[i];

                    IDummyType paraType = types[i];

                    if (paraType == null)
                        throw Exceptions.UnexpectedResultException("input paramter types contains null type.");

                    if (parameter.ParameterType == null)
                    { founded = false; break; }

                    if (!string.Equals(parameter.ParameterType.Namespace, paraType.Namespace))
                    { founded = false; break; }

                    if (!string.Equals(parameter.ParameterType.FullName, paraType.FullName))
                    { founded = false; break; }

                    if (!string.Equals(parameter.ParameterType.Name, paraType.Name))
                    { founded = false; break; }
                }

                if (!founded)
                    continue;

                return method;
            }

            throw new Exception(string.Format("no method existed.type:{0} name:{1}", this.Name, name));
        }

        public IDummyMethodInfo[] GetMethods()
        {
            return methods.ToArray();
        }

        public override string ToString()
        {
            return this.Name;
        }

        public IDummyType Clone()
        {
            DummyType clone = new DummyType(this.Namespace, this.fullName, this.Name);
            if (this.assembly != null)
                clone.assembly = this.assembly.Clone();
            clone.propertyType = this.propertyType;
            clone.dataType = this.dataType;
            clone.isArray = this.isArray;
            clone.isGenericType = this.isGenericType;
            clone.elementType = this.elementType;
            clone.genericArgs = this.genericArgs;
            clone.interfaces = this.interfaces;
            clone.properties.Clear();
            foreach (IDummyType att in GetCustomAttributes())
                clone.AddAttribute(att);
            return clone;
        }

        public override void Dispose()
        {
            this.assembly = null;
            this.properties = null;//不能clear 因为会从structure clone过来
            this.methods = null;
            this.genericArgs = null;
            this.interfaces = null;
            this.elementType = null;
            base.Dispose();
        }

        public override bool Verify()
        {
            base.Verify();

            foreach (IDummyPropertyInfo property in properties)
                property.Verify();

            foreach (IDummyType att in GetCustomAttributes())
                att.Verify();

            foreach (IDummyMethodInfo method in methods)
                method.Verify();

            return true;
        }



        internal void SetElementType(IDummyType type)
        {
            if (type == null)
                throw Exceptions.VerificationFailedException(type);

            this.IsArray = true;
            this.elementType = type;
        }

        internal void AddGenericArgument(IDummyType type)
        {
            if (type == null)
                throw Exceptions.VerificationFailedException(type);

            this.IsGenericType = true;
            this.genericArgs.Add(type);
        }

        internal IDummyAssembly CreateAssembly(string assemblyname, string assemblyfilename)
        {
            IDummyAssembly assembly = new DummyAssembly(assemblyname, assemblyfilename);

            this.assembly = assembly;

            return assembly;
        }

        /// <summary>
        /// 需要后期初始化attribute / propertyType
        /// </summary>
        /// <param name="declaringType"></param>
        /// <param name="metadataToken"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        [CreateProperty]
        internal IDummyPropertyInfo CreateProperty(string name, bool canread, bool canwrite)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name, canread, canwrite);

            LoggerHelper.Log("-> DummyType.CreateProperty({0},{1},{2});", name, canread, canwrite);
            DummyPropertyInfo property = new DummyPropertyInfo();
            property.DeclaringType = this;
            property.Name = name;
            property.CanRead = canread;
            property.CanWrite = canwrite;
            properties.Add(property);
            return property;
        }

        /// <summary>
        /// 内部构造property使用
        /// </summary>
        /// <returns></returns>
        [LastProperty]
        internal DummyPropertyInfo LastProperty()
        {
            LoggerHelper.Log("-> DummyType.LastProperty()");
            if (properties.Count == 0)
                return null;

            return properties[properties.Count - 1];
        }

        /// <summary>
        /// 内部构造method使用
        /// </summary>
        /// <returns></returns>
        [LastMethod]
        internal DummyMethodInfo LastMethod()
        {
            LoggerHelper.Log("-> DummyType.LastMethod()");

            if (methods.Count == 0)
                return null;

            return methods[methods.Count - 1];
        }

        [AddInterface]
        internal void AddInterface(IDummyType type)
        {
            if (type == null)
                throw Exceptions.VerificationFailedException(type);

            LoggerHelper.Log("-> DummyType.AddInterface({0})", type);
            this.interfaces.Add(type);
        }

        [CreateMethod]
        internal IDummyMethodInfo CreateMethod(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw Exceptions.VerificationFailedException(name);

            LoggerHelper.Log("-> DummyType.CreateMethod({0})", name);
            DummyMethodInfo method = new DummyMethodInfo();
            method.DeclaringType = this;
            method.Name = name;
            methods.Add(method);
            return method;
        }


        internal class AssemblyAttribute : Attribute { }
        internal class CreatePropertyAttribute : Attribute { }
        internal class LastPropertyAttribute : Attribute { }
        internal class LastMethodAttribute : Attribute { }
        internal class AddInterfaceAttribute : Attribute { }
        internal class CreateMethodAttribute : Attribute { }
    }
}
